//	+----------------------------------------------+
//	| SVMBR - A program for training SVMs          |
//	+----------------------------------------------+
//	| * Creator, modeling, coding:                 |
//	|    Marcelo Barros de Almeida                 |
//	|    barros@smar.com.br                        |
//	|    http://litc.cpdee.ufmg.br/~barros/        |
//	| * Coding, improvements, bug fixes:           |
//	|    Bernardo Penna                            |
//	|    bprc@brfree.com.br                        |
//	+----------------------------------------------+
//
//	 Copyright(c) 2002 by Marcelo Barros de Almeida
//	                            All rights reserved
//
//	-[CVS]------------------------------------------
//	$Author$
//	$Date$
//	$Source$
//	$Name$
//	$Revision$
//	------------------------------------------------

#include <iostream>
#include <stdlib.h>
#include "global.h"
#include "svm.h"
#include "lineargs.h"
#include "cronometer.h"
#include "smosolver.h"
#include "edrsolver.h"
#include "boostsolver.h"

int main(int argn, char ** argv) {

	INT		 pattern;
	Data     *data;
	Kernel   *kernel;
	Solver   *solver;
	SVM      *svm; 

  // Message
  cout <<	
		"+----------------------------------------------+\n"
		"| SVMBR - A program for training SVMs          |\n"
		"+----------------------------------------------+\n"
		"| * Creator, modeling, coding:                 |\n"
		"|    Marcelo Barros de Almeida                 |\n"
		"|    barros@smar.com.br                        |\n"
		"|    http://litc.cpdee.ufmg.br/~barros/        |\n"
		"| * Coding, improvements, bug fixes:           |\n"
		"|    Bernardo Penna                            |\n"
		"|    bprc@brfree.com.br                        |\n"
		"+----------------------------------------------+\n"
		"\n"
 		" Copyright(c) 2002 by Marcelo Barros de Almeida.\n"
		"                            All rights reserved.\n"
		"\n";

	LineArgs *args = new LineArgs(argn,argv);
	
	if(args->hasTraining()){

		// Before you complain: I am very tired to write so many get function
		// for LineArgs class. 

		// data type
		if(args->dataType == Data::NORMAL)
			data = new NormalData(args->trainPattFile);
		else if(args->dataType == Data::SPARSE)
			 data = new SparseData(args->trainPattFile);
		else if(args->dataType == Data::BINARY)
			data = new BinaryData(args->trainPattFile);

		for (pattern = 0; pattern < (args->numClasses > 2? args->numClasses : 1); pattern++){

		  svm = new TrainSVM();

		  svm->setData(data);

		  if (args->numClasses > 2)
		    data->setTarget(pattern);

		  // kernel type
		  if(args->params[pattern]->kernelType == Kernel::RBF)
		 	kernel = new RBFKernel(args->params[pattern]->p1,
				        args->params[pattern]->p2,
						args->params[pattern]->p3,
						data);
	  	  else if(args->params[pattern]->kernelType == Kernel::POLY)
			kernel = new PolyKernel(args->params[pattern]->p1,
						args->params[pattern]->p2,
						args->params[pattern]->p3,
						data);
		  else if(args->params[pattern]->kernelType == Kernel::LINEAR)
			kernel = new LinearKernel(args->params[pattern]->p1,
						  args->params[pattern]->p2,
						  args->params[pattern]->p3,
						  data);
		  else if(args->params[pattern]->kernelType == Kernel::SIGMOID)
                      kernel = new SigmoidKernel(args->params[pattern]->p1,
									  args->params[pattern]->p2,
									  args->params[pattern]->p3,
									  data);

		  svm->setKernel(kernel);

		  // solver method
		  if(args->solverMethod == Solver::EDR)
				solver = new EDRSolver(args->params[pattern]->C,
											args->params[pattern]->tol,
											args->params[pattern]->epsilon,
											args->params[pattern]->numEDR,
											args->params[pattern]->EDRPower,
											svm,
											kernel,
											data,
											args->chunkSize); 
      else if(args->solverMethod == Solver::SMO)
					solver = new SMOSolver(args->params[pattern]->C,
												args->params[pattern]->tol,
												args->params[pattern]->epsilon,
												svm,
												kernel,
												data,
												args->chunkSize);
			else if(args->solverMethod == Solver::BOOSTING)
					solver = new BoostingSolver(args->params[pattern]->C,
												args->params[pattern]->tol,
												args->params[pattern]->epsilon,
												svm,
												kernel,
												data,
												args->chunkSize);

		  svm->setSolver(solver);

		  svm->setsvmFile(args->params[pattern]->svmFile);

		  // running
		  svm->run();
		  if (args->numClasses > 2) 
				cout << endl << "Running class " << pattern << " ..." << endl;

		  svm->printSummary();

		  delete kernel;
		  delete solver;
		  delete svm;

		}
		delete data;
	}

	if(args->hasTesting()){

		// two classes: standard testing phase
		if(args->numClasses == 2) {
			svm = new TestSVM(args->params[0]->svmFile,
												args->testPattFile,
												args->testOutFile,
												args->params[0]->limit);
			svm->run();
			svm->printSummary();
			delete svm;
		} 
		else {
			// many classes: max output among classes 
			REAL **result;
			INT *correctClass, *outClass;
			REAL maxOut;
			INT numTestExamples, numCorrect, i, idx;
			INT dataType;
			
			for (pattern = 0; pattern < args->numClasses ; pattern++){
				svm = new TestSVM(args->params[pattern]->svmFile,args->testPattFile,(CHAR*)"",args->params[pattern]->limit);			
				cout << "Checking for class " << pattern << " ..." << endl;  
				// get num of test examples
				numTestExamples = (svm->getData())->getnumExamples();
				// allocates vectors: just once
				if (!pattern){
					try {
						result = new REAL *[args->numClasses];
						correctClass = new INT [numTestExamples];
						outClass = new INT [numTestExamples];
						for(i = 0 ; i < args->numClasses ; i++)
							result[i] = new REAL [numTestExamples];
					}
					catch(...){
						cerr << "Error when allocating memory (::hasTesting)." << endl;
						exit(1);
					}		
					// filling with correct values
					for(i = 0; i < numTestExamples ;i++)
						correctClass[i] = (svm->getData())->gettarget(i);
					// saving data type
					dataType = (svm->getData())->getdataType();
				}

				(svm->getData())->setTarget(pattern);
				svm->run();

				for (INT i = 0; i < numTestExamples ; i++)
					result[pattern][i] = ((TestSVM *) svm)->getevalTarget(i);	
				
				delete svm;

			}
		
			//checking outputs for multi class
			numCorrect = 0;
			for (i = 0 ; i < numTestExamples ; i++){
				maxOut = -1;
				idx = -1;
				for(pattern = 0 ; pattern < args->numClasses; pattern++){
					if (result[pattern][i] > maxOut){
						maxOut = result[pattern][i];					
						idx = pattern;
					}
				}
				outClass[i] = idx;
				if(correctClass[i] == idx)
					numCorrect++;
			}
			
			//saving test results in file testOutFile for multi class
			ofstream file(args->testOutFile); 

			if(!file.is_open()){
				cerr << "(::hasTesting) Error when opening " << args->testOutFile << endl;
				exit(1);
			}

			cout << "Saving outputs..." << endl << flush;
 
			// line with "#" as first character are comments
			file << "###### SVMBR file version:" << endl;
			file << SVM::SVMBR_VERSION << endl;	

			file << "##### Number of input testing patterns: " << endl;
			file << numTestExamples << endl; 

			file << "##### Number of classes: " << endl;
			file << args->numClasses << endl;

			CHAR tmp[40];
			sprintf(tmp,"%02.4f%%",100.0*numCorrect/numTestExamples);
			file << "##### Number of well classified patterns: (" << tmp << ")" << endl; 
			file << numCorrect << endl;

			file << "##### Correct outputs:" << endl;
			for(i = 0 ; i < numTestExamples ; i++)
				file << correctClass[i] << endl;

			file << "##### SVM class output (first class is 0):" << endl;
			for(i = 0 ; i < numTestExamples ; i++)
				file << outClass[i] << endl;

			file.close();
			
  		//printing test summary of multi class generalization
			cout << endl << "# Testing result:" << endl;
			cout <<         "# ===============" << endl;
			cout << "# Data representation: ";
			if(dataType == Data::NORMAL)      
				cout << "Normal" << endl;
			else if(dataType == Data::SPARSE)	
				cout << "Sparse" << endl;
			else if(dataType == Data::BINARY) 
				cout << "Binary" << endl;
	  		
			cout << "# Number of input testing patterns: " << numTestExamples << endl; 
			cout << "# Number of well classified patterns: "  << numCorrect << " (" << tmp << ")" << endl; 

			// freeing space
			delete correctClass;
			delete outClass;
			for(i = 0 ; i < args->numClasses ; i++)
				delete result[i];
			delete result;
		}
	}

	delete args;
	return TRUE;	
}

	


